package ar.edu.caece.pl.asin.model.impl;

import ar.edu.caece.pl.alex.model.IToken;
import ar.edu.caece.pl.alex.model.impl.Token;
import ar.edu.caece.pl.asin.manager.IAnalizadorSintactico;
import ar.edu.caece.pl.asin.manager.impl.AbstractAnalizadorSintactico;
import ar.edu.caece.pl.asin.model.ITokenStream;

public class ProcsFuncs extends AbstractAnalizadorSintactico implements IAnalizadorSintactico {
	
	/* INICIALIZACION */	
	public ProcsFuncs ( ITokenStream ts ) {
		super(ts, false);
	}
	public ProcsFuncs ( ITokenStream ts, boolean debugMode ) {
		super(ts,debugMode);
	}
	
	
	/** 
	 * Devuelve true si reconoce un bloque de procedimientos y funciones
	 */
	public boolean reconocer () {
		
		boolean recognized = true; //(asumimos correctitud hasta demostrar lo contrario)
		
		//----------------------------------------------
		//Agrego info de debug de la produccion
		em.debug( "Reconocer ProcsFuncs:" );		//TODO pasarlo a accion semantica
		//----------------------------------------------
		
		/* <PROCS_FUNCS> ->
		 *		PROCEDURE IDENTIFICADOR(<BLOQUE_VAR_PARAM>);        <HEADER> BEGIN <SENTENCIA> END-PROC       ; <PROCS_FUNCS> |
		 *		FUNCTION  IDENTIFICADOR(<BLOQUE_VAR_PARAM>): <TIPO>;<HEADER> BEGIN <SENTENCIA> END FUNC <EXP> ; <PROCS_FUNCS> */
		
		if ( this.getTokenSiguiente().equals( new Token(IToken.PALABRA_RESERVADA_PROCEDURE) ) ) {

			// <PROCS_FUNCS> -> PROCEDURE IDENTIFICADOR ( <BLOQUE_VAR_PARAM> ) ; <HEADER> BEGIN <SENTENCIA> END-PROC ; <PROCS_FUNCS>

			//----------------------------------------------
			//Agrego info de debug de la produccion
			em.debug( "<PROCS_FUNCS> -> PROCEDURE IDENTIFICADOR ( <BLOQUE_VAR_PARAM> ) ; <HEADER> BEGIN <SENTENCIAS> END-PROC ; <PROCS_FUNCS>", this.getTokenSiguiente() );
			//----------------------------------------------

			// Reconocer Palabra Reservada "procedure"
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_PROCEDURE) ) {
				//El reconocerToken() habria adelantado el tokenStream hasta salir de esta iteracion del ProcsFuncs,
				//entonces no hay que seguir buscando sino salir
				return false;
			}
			
			// Reconocer Identificador
			if ( !this.reconocerToken(IToken.TYPE_IDENTIFICADOR) ) {
				return false;
			}
			
			// Reconocer Parentesis izquierdo
			if ( !this.reconocerToken(IToken.TYPE_PARENTESIS_IZQ) ) {
				return false;
			}
			
			// Reconocer BloqueVarParam
			while ( BloqueVarParam.primeros( this.getTokenSiguiente() ) ) {
				IAnalizadorSintactico bloqueVarParam = new BloqueVarParam(this.tokenStream, this.debugMode);
				recognized &= bloqueVarParam.reconocer();
			}
			
			// Reconocer Parentesis derecho
			if ( !this.reconocerToken(IToken.TYPE_PARENTESIS_DER) ) {
				return false;
			}
			
			// Reconocer punto y coma
			if ( !this.reconocerToken(IToken.TYPE_PUNTO_COMA) ) {
				return false;
			}
			
			// Reconocer <HEADER>
			while ( Header.primeros( this.getTokenSiguiente() ) ) {
				
				IAnalizadorSintactico header = new Header(this.tokenStream, this.debugMode);
				recognized &= header.reconocer();
			}
			
			// Reconocer token begin
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_BEGIN) ) {
				return false;
			}
			
			// Reconocer varias <SENTENCIA>
			while ( Sentencia.primeros( this.getTokenSiguiente() ) ) {
				
				IAnalizadorSintactico sentencia = new Sentencia(this.tokenStream, this.debugMode);
				recognized &= sentencia.reconocer();
			}
						
			// Reconocer end-proc
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_END_PROC) ) {
				return false;
			}
			
			// Reconocer punto y coma
			if ( !this.reconocerToken(IToken.TYPE_PUNTO_COMA) ) {
				return false;
			}
			//LISTO: cierra el bucle del AnalizadorSintactico
			
		} else if ( this.getTokenSiguiente().equals( new Token(IToken.PALABRA_RESERVADA_FUNCTION) ) ) {

			// <PROCS_FUNCS> -> FUNCTION  IDENTIFICADOR ( <BLOQUE_VAR_PARAM> ) : <TIPO> ; <HEADER> BEGIN <SENTENCIA> END FUNC <EXP> ; <PROCS_FUNCS>
			
			//----------------------------------------------
			//Agrego info de debug de la produccion
			em.debug( "<PROCS_FUNCS> -> FUNCTION  IDENTIFICADOR(<BLOQUE_VAR_PARAM>): <TIPO>;<HEADER> BEGIN <SENTENCIAS> END FUNC <EXP> ; <PROCS_FUNCS>", this.getTokenSiguiente() );
			//----------------------------------------------

			// Reconocer Palabra Reservada "function"
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_FUNCTION) ) {
				//El reconocerToken() habria adelantado el tokenStream hasta salir de esta iteracion del ProcsFuncs,
				//entonces no hay que seguir buscando sino salir
				return false;
			}
			
			// Reconocer Identificador
			if ( !this.reconocerToken(IToken.TYPE_IDENTIFICADOR) ) {
				return false;
			}
			
			// Reconocer Parentesis izquierdo
			if ( !this.reconocerToken(IToken.TYPE_PARENTESIS_IZQ) ) {
				return false;
			}
			
			// Reconocer BloqueVarParam
			while ( BloqueVarParam.primeros( this.getTokenSiguiente() ) ) {
				IAnalizadorSintactico bloqueVarParam = new BloqueVarParam(this.tokenStream, this.debugMode);
				recognized &= bloqueVarParam.reconocer();
			}
			
			// Reconocer Parentesis derecho
			if ( !this.reconocerToken(IToken.TYPE_PARENTESIS_DER) ) {
				return false;
			}
			
			// Reconocer dos puntos
			if ( !this.reconocerToken(IToken.TYPE_DOS_PUNTOS) ) {
				return false;
			}

			// Reconocer <tipo>
			IAnalizadorSintactico tipo = new Tipo(this.tokenStream, this.debugMode);
			recognized &= tipo.reconocer();
			
			// Reconocer punto y coma
			if ( !this.reconocerToken(IToken.TYPE_PUNTO_COMA) ) {
				return false;
			}
			
			// Reconocer <HEADER>
			while ( Header.primeros( this.getTokenSiguiente() ) ) {
				
				IAnalizadorSintactico header = new Header(this.tokenStream, this.debugMode);
				recognized &= header.reconocer();
			}
			
			// Reconocer token begin
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_BEGIN) ) {
				return false;
			}
			
			// Reconocer varias <SENTENCIA>
			while ( Sentencia.primeros( this.getTokenSiguiente() ) ) {
				
				IAnalizadorSintactico sentencia = new Sentencia(this.tokenStream, this.debugMode);
				recognized &= sentencia.reconocer();
			}	
			
			// Reconocer end-func
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_END_FUNC) ) {
				return false;
			}
			
			// Reconocer Exp
			if ( Expresion.primeros(this.getTokenSiguiente())) {
				
				IAnalizadorSintactico exp = new Expresion(this.tokenStream, this.debugMode);
				recognized &= exp.reconocer();
				
			} else {
				
				em.error("Se esperaba Expresion", this.getTokenSiguiente());
				
				//Tratamiento de errores
				em.recover(this.getClass(), this.tokenStream);
				
				return false;
			}
			
			// Reconocer punto y coma
			if ( !this.reconocerToken(IToken.TYPE_PUNTO_COMA) ) {
				return false;
			}
			//LISTO: cierra el bucle del AnalizadorSintactico
			
			/** Fin de ReconocerFuntion*/
			
		}
		
		return recognized;
	}
	
	public static boolean primeros(IToken token) {
		return  token.equals( new Token(IToken.PALABRA_RESERVADA_PROCEDURE) ) ||
				token.equals( new Token(IToken.PALABRA_RESERVADA_FUNCTION) );
	}
}
